package com.wigem.util;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.session.Configuration;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.*;
import java.util.stream.Collectors;

public class MappingResultUtils {

    private static String UNDERLINE = "_";


    /**
     * 根据实体类的属性名获取对应的数据表的字段列名
     *
     * @param property 属性名
     * @param clazz    实体类
     * @return 字段名
     */
    public static String property2Field(String property, Class<?> clazz) {
        if (StringUtils.isEmpty(property)) {
            return "";
        }
        ResultMap resultMap = getBaseResultMap(clazz);
        if (ObjectUtils.isNotEmpty(resultMap)) {
            for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {
                if (resultMapping.getProperty().equals(property)) {
                    property = resultMapping.getColumn();
                    return property;
                }
            }
        }
        //找不到resultMap就转下划线处理
        String column = camelToUnderline(property, false);
        System.out.println("没有查询到Mapper中的ResultMap：" + property + "字段映射信息!将使用驼峰命名法转换下划线属性名:" + column);
        return column;
    }

    /**
     * 获取实体类对应的mybatis mapper的resultMap映射对象
     * 必须保证jdk1.8及以上
     *
     * @param clazz 实体类
     * @return ResultMap
     */
    private static ResultMap getBaseResultMap(Class<?> clazz) {
        //获取SqlSessionTemplate
        SqlSessionTemplate sqlSessionTemplate = ApplicationUtils.getBean(SqlSessionTemplate.class);
        assert sqlSessionTemplate != null;
        //关键在于这里，获取SqlSessionTemplate中的Configuration，这里面当前Sql seesion会话的所有参数
        //Configuration的getResultMap方法就可以获取指定的ResultMap，所以是该方法需要指定ResultMap的ID
        Configuration configuration = sqlSessionTemplate.getConfiguration();
        //获取所有的ResultMap的名字：以xml的命名空间如：xx.xx.dao.StudentMapper加resultMap的id如：BaseResultMap组合：xx.xx.dao.StudentMapper.BaseResultMap这样的全定名
        //注意会存在一个默认的BaseResultMap,为上面那个的短名称，所以我们会拿到项目所有的ResultMap
        Collection<String> resultMapNames = configuration.getResultMapNames();

        //利用Stream流快速筛查
        List<ResultMap> resultMaps = resultMapNames.parallelStream()
                .filter(name -> name.contains("."))//要全定名不要短名
                .map(configuration::getResultMap)//根据全定名找到匹配的ResultMap
                .filter(resultMap -> Objects.equals(resultMap.getType(), clazz))//匹配xml中type属性和实体类一致的
                //排序,按字段数量来；这里还是会有多个，为什么：比如上面的xml中就有两个ResultMap
                .sorted(Comparator.comparing(resultMap -> resultMap.getPropertyResultMappings().size()))
                .collect(Collectors.toList());
        //翻转，毕竟resultMap包含的字段多的属性映射更全嘛
        Collections.reverse(resultMaps);
        //找出那个type属性一致的，其实这个list里面所有的resultMap属性都是一致的了，毕竟上面过滤了，只不过Stream过滤就算只有一个也是那list装的
        if (ObjectUtils.isNotEmpty(resultMaps)) {
//            return resultMaps.get(0);
            for (ResultMap resultMap : resultMaps) {
                Class<?> type = resultMap.getType();
                if (Objects.equals(type, clazz)) {
                    return resultMap;
                }
            }
        }
        return null;
    }

    /**
     * 驼峰转下划线
     *
     * @param param     字符串
     * @param upperCase 是否全大写
     * @return 结果
     */
    public static String camelToUnderline(String param, boolean upperCase) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append(UNDERLINE);
            }
            if (upperCase) {
                //统一都转大写
                sb.append(Character.toUpperCase(c));
            } else {
                //统一都转小写
                sb.append(Character.toLowerCase(c));
            }
        }
        return sb.toString();
    }


}
